package com.gac.wms.workflow.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.gac.wms.workflow.custom.CustomProcessDiagramGenerator;
import com.gac.wms.workflow.service.IProcessImageService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

@Service
@Slf4j
public class ProcessImageServiceImpl implements IProcessImageService {

    @Autowired
    HistoryService historyService;

    @Autowired
    RepositoryService repositoryService;

    // 获取节点根据条件显示
    public void getIncomeNodesRecur(String currentNodeId, List<Map<String,String>> nodeList, org.activiti.bpmn.model.Process process, boolean isAll) {
        FlowElement currentFlowElement = process.getFlowElement(currentNodeId);
        Map<String,String> node = new HashMap<>();
        List<SequenceFlow> outgoingFlows = null;
        if (currentFlowElement instanceof UserTask) {
            outgoingFlows = ((UserTask) currentFlowElement).getOutgoingFlows();
        } else if (currentFlowElement instanceof Gateway) {
            outgoingFlows = ((Gateway) currentFlowElement).getOutgoingFlows();
        } else if (currentFlowElement instanceof StartEvent) {
            outgoingFlows = ((StartEvent) currentFlowElement).getOutgoingFlows();
        } else if (currentFlowElement instanceof EndEvent) {
            outgoingFlows = ((EndEvent) currentFlowElement).getOutgoingFlows();
        }
        if (outgoingFlows != null && outgoingFlows.size() > 0) {
            outgoingFlows.forEach(incomingFlow -> {
                // 条件表达式
                String expression = incomingFlow.getConditionExpression();
                // 出线的下一节点
                String targetRefElementID = incomingFlow.getTargetRef();
                // 查询下一节点的信息
                FlowElement preFlowElement = process.getFlowElement(targetRefElementID);
                //用户任务
                if (preFlowElement instanceof UserTask) {
                    UserTask userTask= ((UserTask) preFlowElement);
                    node.put(userTask.getId(),userTask.getId());
                    nodeList.add(node);
                    if (isAll) {
                        getIncomeNodesRecur(preFlowElement.getId(), nodeList, process, true);
                    }
                } else if (preFlowElement instanceof ExclusiveGateway) {
                    //排他网关
                    getIncomeNodesRecur(preFlowElement.getId(), nodeList, process, isAll);
                } else if (preFlowElement instanceof ParallelGateway) {
                    //并行网关
                    getIncomeNodesRecur(preFlowElement.getId(), nodeList, process, isAll);
                } else if(preFlowElement instanceof EndEvent){
                    getIncomeNodesRecur(preFlowElement.getId(), nodeList, process, isAll);
                }
            });
        }
    }

    private String getCurrentNodeId(String procInstId){
        List<HistoricActivityInstance> historicActivityInstances = getNewHistoricActivityInstances(procInstId,true);
        String currentNodeId="";
        if(CollectionUtil.isNotEmpty(historicActivityInstances)) {
            // 当前节点
            currentNodeId=historicActivityInstances.get(historicActivityInstances.size()-1).getActivityId();
        }
        return currentNodeId;
    }

    @Override
    public void getFlowImgByProcInstId(String procInstId, OutputStream outputStream, boolean useCustomColor,String state) throws Exception {
        try {
            // 获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService
                    .createHistoricProcessInstanceQuery()
                    .processInstanceId(procInstId).singleResult();
            List<HistoricActivityInstance> historicActivityInstances = getNewHistoricActivityInstances(procInstId,false);
            String currentNodeId=getCurrentNodeId(procInstId);
            // 查询模型节点
            BpmnModel bpmnModel = repositoryService
                    .getBpmnModel(historicProcessInstance.getProcessDefinitionId());
            List<HistoricActivityInstance> historicActivityInstancesList = new ArrayList<>(16);
            for(HistoricActivityInstance historicActivityInstance:historicActivityInstances){
                historicActivityInstancesList.add(historicActivityInstance);
                if(currentNodeId.equals(historicActivityInstance.getActivityId())){
                    break;
                }
            }
            // 高亮已经执行流程节点ID集合
            List<String> highLightedActivitiIds = new ArrayList<>();
            // 定义自定义颜色标记
            AtomicInteger index = new AtomicInteger(1);
            // 所有节点执行完成
            historicActivityInstancesList.forEach(hai->{
                if("endEvent".equals(hai.getActivityType())){
                    index.set(0);
                    return;
                }
            });
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstancesList) {
                if (useCustomColor) {
                    // 用自定义颜色
                    if (index.get() == historicActivityInstancesList.size()) {
                        // 当前节点
                        highLightedActivitiIds.add(historicActivityInstance.getActivityId() + "#");
                    } else {
                        // 已完成节点
                        highLightedActivitiIds.add(historicActivityInstance.getActivityId());
                    }
                } else {
                    // 用默认颜色
                    highLightedActivitiIds.add(historicActivityInstance.getActivityId());
                }
                index.getAndIncrement();
            }
            ProcessDiagramGenerator processDiagramGenerator = null;
            if (useCustomColor) {
                // 使用自定义的程序图片生成器
                processDiagramGenerator = new CustomProcessDiagramGenerator();
            } else {
                // 使用默认的程序图片生成器
                processDiagramGenerator = new DefaultProcessDiagramGenerator();
            }
            // 高亮流程已发生流转的线id集合(驳回处理)
            List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstancesList);

            // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
            InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel,
                    highLightedActivitiIds, highLightedFlowIds, "宋体",
                    "微软雅黑", "黑体");
            // 输出图片内容
            Integer byteSize = 1024;
            byte[] b = new byte[byteSize];
            int len;
            while ((len = imageStream.read(b, 0, byteSize)) != -1) {
                outputStream.write(b, 0, len);
            }
        } catch (Exception e) {
            log.error("processInstanceId" + procInstId + "生成流程图失败，原因：" + e.getMessage(), e);
        }
    }

    // 获取新的节点
    private List<HistoricActivityInstance> getNewHistoricActivityInstances(String procInstId,boolean flag){
        // 获取流程中已经执行的节点，按照执行先后顺序排序
        List<HistoricActivityInstance> historicActivityInstances = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(procInstId)
                .orderByHistoricActivityInstanceId()
                .orderByHistoricActivityInstanceStartTime()
                .asc().list();
        List<HistoricActivityInstance> newList = new ArrayList<>();
        // 按时间排序后以ActivityId分组
        Map<String, List<HistoricActivityInstance>> collectMap;
        if(flag) {
            collectMap = historicActivityInstances.stream()
                    .sorted(Comparator.comparing(HistoricActivityInstance::getStartTime).reversed())
                    .collect(Collectors.groupingBy(HistoricActivityInstance::getActivityId));
        }else{
            collectMap = historicActivityInstances.stream()
                    .sorted(Comparator.comparing(HistoricActivityInstance::getStartTime))
                    .collect(Collectors.groupingBy(HistoricActivityInstance::getActivityId));
        }
        // @TODO 这里要观察多次驳回的情况
        for(Map.Entry<String, List<HistoricActivityInstance>> entry: collectMap.entrySet()){
            List<HistoricActivityInstance> value = entry.getValue();
            if(value.size()>1){
                newList.add(entry.getValue().get(0));
            }else{
                newList.add(entry.getValue().get(0));
            }
        }
        if(newList.size()>1){
            List<HistoricActivityInstance> collect = newList.stream().sorted(Comparator.comparing(HistoricActivityInstance::getStartTime))
                    .collect(Collectors.toList());
            return collect;
        }else{
            return historicActivityInstances;
        }
    }

    /**
     * 获取已经流转的线
     *
     * @param bpmnModel
     * @param historicActivityInstances
     * @return
     */
    private static List<String> getHighLightedFlows(BpmnModel bpmnModel,
                                                    List<HistoricActivityInstance> historicActivityInstances) {
        // 高亮流程已发生流转的线id集合
        List<String> highLightedFlowIds = new ArrayList<>();
        // 全部活动节点
        List<FlowNode> historicActivityNodes = new ArrayList<>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();
        // 去重
        historicActivityInstances=historicActivityInstances.stream().collect(collectingAndThen(
                toCollection(() -> new TreeSet<>(
                        Comparator.comparing(HistoricActivityInstance::getActivityId))), LinkedList::new));
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess()
                    .getFlowElement(historicActivityInstance.getActivityId(), true);
            historicActivityNodes.add(flowNode);
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstances.add(historicActivityInstance);
            }
        }
        FlowNode currentFlowNode = null;
        FlowNode targetFlowNode = null;
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess()
                    .getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
            /**
             * 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转：
             * 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
             * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转
             */
            if ("parallelGateway".equals(currentActivityInstance.getActivityType())
                    || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配流程目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess()
                            .getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicActivityNodes.contains(targetFlowNode)) {
                        highLightedFlowIds.add(targetFlowNode.getId());
                    }
                }
            } else {
                List<Map<String, Object>> tempMapList = new ArrayList<>();
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            Map<String, Object> map = new HashMap<>(16);
                            map.put("highLightedFlowId", sequenceFlow.getId());
                            map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
                            tempMapList.add(map);
                        }
                    }
                }
                if (!CollectionUtils.isEmpty(tempMapList)) {
                    // 遍历匹配的集合，取得开始时间最早的一个
                    long earliestStamp = 0L;
                    String highLightedFlowId = null;
                    for (Map<String, Object> map : tempMapList) {
                        long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
                        if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
                            highLightedFlowId = map.get("highLightedFlowId").toString();
                            earliestStamp = highLightedFlowStartTime;
                        }
                    }
                    highLightedFlowIds.add(highLightedFlowId);
                }

            }
        }
        return highLightedFlowIds;
    }
}
